iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Mobile Development

新手向Android&Kotlin學習紀錄30天系列 第 19

第19天 Kotlin小學堂(8) : 函數類型、Lambda

  • 分享至 

  • xImage
  •  

今天要介紹很常在Kotlin 中看見的寫法,lambda,直接開始吧!

lambda

lambda可以當作函數類型(function type) 來使用,意思是可以當作宣告變數指定的類型、當引數傳入另一個函數和函數回傳值,用法十分靈活。

豆知識:為什麼叫lambda?lambda可以用希臘字母「λ」來表示,是lambda演算的簡稱。lambda演算是一套數學演算邏輯,...。引自:Kotlin權威2.0

簡略語法

若一個函數的lambda參數排在最後或是唯一的參數,可省略lambda引數的一對小括號,比如String的count函數:

//原本:
"barbiqla".count({it=='b'}) 
//簡略
"barbiqla".count{it=='b'}

將函數儲存在變數中

函數參照

要想將函數當作值存入變數便要使用 ::運算子

fun main() {
    val goodMorningGreeting = ::goodMorning
    goodMorningGreeting()  // 控制台印出"good morning."
}
fun goodMorning(){
    println("good morning.")
}

使用lambda重構函數

使用val或var關鍵字重構 goodMorning function,如此賦值給goodMorningGreeting時便不需要::運算子,並且呼叫兩者並運行,系統會執行 lambda 運算式的本體,印出"good morning."

fun main() {
    val goodMorningGreeting = goodMorning
    goodMorningGreeting() // 控制台印出"good morning."
    goodMorning()    // 控制台印出"good morning."
}
val goodMorning = {
    println("good morning.")
}

使用函數做為資料類型

函數類型不是函數還分了很多種類型,意思是可以像資料類型這樣當作一類類型來使用。

語法:


寫一個goodnight變數的資料類型指定為() -> Unit來比較,不寫的類型也一樣是() -> Unit
若是換成參數要整數、回傳值是整數的函數類型就寫為(Int)->Int

val goodMorning = {
    println("good morning.")
}
val goodNight:()->Unit = {
    println("good night.")
}

將函數作為回傳類型

既然可以當做資料類型使用,那麼可以當回傳值的類型也不難理解吧。

語法:

將早晚問候整合成一個 greeting function:

//整合早安晚安的問候function,返回函數類型`()->Unit`:
fun greeting(isDay:Boolean):()->Unit{
    if(isDay){
        return goodMornig
    }else{
        return goodNight
    }
}
//早安和晚安的變數:
val goodMorning = {
    println("good morning.")
}
val goodNight:()->Unit = {
    println("good night.")
}

將函數做為引數傳遞至其他函數

語法

在函數()中加入參數名並指定型別,

如下列,加入飲品參數,但參數中並沒有指明哪一類飲品,並設定只有早上有飲料喝。而參數類型(Int)->String也只須給類型就好,不必再為它們取名字。
註:下面程式碼設定傳入Int跟數量1只為了方便接下來的說明。

fun greeting(isDay: Boolean,drink : (Int)->String): () -> Unit {
    if (isDay) {
        println(drink(1)) 
        return goodMorning
    } else {
        return goodNight
    }
}

接下來定義上面飲品函數並儲存在變數milk與wine中:

可以看見milk的部分將參數Int可以自訂名為"number",而wine的部分無自訂名稱的話,kotlin會以"it"來代指參數Int

fun main() {
    val milk :(Int)->String = {number ->
        "have $number glass of milk."
    }
    val wine :(Int)->String = {
        "have $it glass of wine."
    }
    val goodMorningGreeting = greeting(true,milk)
    val goodNightGreeting = greeting(false,wine)
    goodMorningGreeting()
    goodNightGreeting()
}

像本例,晚上並沒有要喝飲料,其實不必硬傳飲料引數給他,但是greeting function 要求必須給該如何是好?
這邊就要講到空值null的概念(下面有簡述)。
在資料類型上加問號(如:Int?、String?)來表示這東西可能為空,而函數類型也可以依樣畫葫蘆,加上問號表示可空,在greeting function的drink參數加上表示可空的問號:

fun greeting(
    isDay: Boolean,
    drink: ((Int) -> String)?= null  //將函數類型括號起來打問號
): () -> Unit {
    if (isDay) {
//因應drink的可空性必須做出處理,使它判斷不為空時才執行印出字串,否則編譯不過。
        if (drink != null) println(drink(1)) 
        return goodMorning
    } else {
        return goodNight
    }
}

註 :
null : Null維基百科,是一個概念,表示不存在或無意義的值。
(空引用的發明者Tony Hoare自稱是價值百億美元的錯誤發明)
可空性:kotlin在資料類型上加問號(如:Int?、String?)來表示這東西可能、可以為空,並會要求開發人員對可空性的物件做出處理,這是Kotlin為了對應null所可能導致的異常而做出的設計。
NullPointException引起的問題多廣泛,由知名的stackoverflow網站上,什麼是NullPointException,怎麼修復它這個問題已經被瀏覽的次數可見一斑。


drink參數設好可空性後,晚上就可以不必傳入飲品了:

val goodNightGreeting = greeting(false)

完整的程式碼:

fun main() {
    val milk :(Int)->String= {
        "have $it glass of milk."
    }
    // val wine :(Int)->String= {
    //     "have $it glass of wine."
    // }
    val goodMorningGreeting = greeting(true,milk)
    val goodNightGreeting = greeting(false)
    goodMorningGreeting()
    goodNightGreeting()
    //結果: have 1 glass of milk.
    //      good morning.
    //      good night.
}

fun greeting(
    isDay: Boolean,
    drink: ((Int) -> String)?= null
): () -> Unit {
    if (isDay) {
        if (drink != null) println(drink(1))
        return goodMorning
    } else {
        return goodNight
    }
}

val goodMorning = {
    println("good morning.")
}

val goodNight: () -> Unit = {
    println("good night.")
}

將 lambda 運算式直接傳遞至函數

像milk()只用在一個地方,並不需要重複使用的情況,而且lambda運算是只是一個常值的話(如:1是Int常值、true是布林值常值、"hello"是String常值),可以將lambda運算式直接傳入函數,直接看例子比較快:

    //原本
    val milk :(Int)->String= { "have $it glass of milk."}
    val goodMorningGreeting = greeting(true,milk)
    //lambda傳入函數
    // 刪除milk()
    val goodMorningGreeting = greeting(true,{"have $it glass of milk."}) 
    
    //照一開說過的簡略語法,當lambda是唯一或最後一個參數可以移出()
    val goodMorningGreeting = greeting(true){"have $it glass of milk."}
    
    goodMorningGreeting() 
    // 結果: have $it glass of milk. 
    //      good morning.   

參考codelab:在 Kotlin 中使用函式類型和 lambda 運算式
明天見,掰


上一篇
第18天 kotlin小學堂(7) : Function 函數
下一篇
第20天 Kotlin小學堂(9) : 集合
系列文
新手向Android&Kotlin學習紀錄30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言